package com.yy.distributed.lock.redis;

import com.yy.distributed.lock.DLock;
import com.yy.distributed.lock.DistributedLockCallback;
import com.yy.distributed.lock.DistributedLockTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * Write your description here...
 *
 * @author dinghuan
 * @date 2018/7/19
 */
public class JedisDistributedLockTemplate implements DistributedLockTemplate {
    private static final Logger logger = LoggerFactory.getLogger(JedisDistributedLockTemplate.class);
    private JedisLockFactory jedisLockFactory;

    public void setJedisLockFactory(JedisLockFactory jedisLockFactory) {
        this.jedisLockFactory = jedisLockFactory;
    }

    @Override
    public <T> T lock(DistributedLockCallback<T> callback, boolean fairLock) {
        return lock(callback, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT, fairLock, true);
    }

    @Override
    public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit, boolean fairLock, boolean release) {
        DLock lock = getLock(callback.getLockName(), fairLock);
        try {
            lock.lock(leaseTime, timeUnit);
            logger.info("get redis lock. name: [{}], leaseTime: [{}]", callback.getLockName(), leaseTime);
            return callback.process();

        } finally {
            if (release && lock != null) {
                try {
                    lock.unlock();
                } catch (IllegalMonitorStateException e) {
                    logger.warn(e.getMessage(), e);
                }
            }
        }
    }

    @Override
    public <T> T tryLock(DistributedLockCallback<T> callback, boolean fairLock) {
        return tryLock(callback, DEFAULT_WAIT_TIME, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT, fairLock, true);
    }

    @Override
    public <T> T tryLock(DistributedLockCallback<T> callback, long waitTime, long leaseTime, TimeUnit timeUnit, boolean fairLock, boolean release) {
        DLock lock = getLock(callback.getLockName(), fairLock);
        try {
            if (lock.tryLock(waitTime, leaseTime, timeUnit)) {
                logger.info("get redis lock. name: [{}], leaseTime: [{}]", callback.getLockName(), leaseTime);
                return callback.process();
            }
        } finally {
            if (release && lock != null) {
                try {
                    lock.unlock();
                } catch (IllegalMonitorStateException e) {
                    logger.warn(e.getMessage(), e);
                }
            }
        }


        return null;
    }

    private DLock getLock(String name, boolean fairLock) {
        DLock dLock = null;
        if (fairLock) {
            dLock = jedisLockFactory.getFairLock(name);
        } else {
            dLock = jedisLockFactory.getLock(name);
        }
        return dLock;
    }
}
